package com.charon.common.util;

import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;

import javax.crypto.*;
import javax.crypto.spec.SecretKeySpec;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.security.InvalidKeyException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.Base64;

/**
 * @ClassName: CipherUtils
 * @Description: 加密解密工具类
 * @Author Charon [248135724@qq.com]
 * @Date 2018/12/23 15:37
 * @Version V 1.0
 */
public class CipherUtils {

    static final private String SEED_RULES = "charon";

    /**
     * @Description AES加密
     * @Author Charon [248135724@qq.com]
     * @Date 2018/12/23 16:00
     * @Param [content]
     * @return java.lang.String
     */
    static public final String aesEncode(String content) {
        try {
            //1.构造密钥生成器，指定为AES算法,不区分大小写
            KeyGenerator keyGenerator = KeyGenerator.getInstance("AES");
            //2.根据ecnodeRules规则初始化密钥生成器
            //生成一个128位的随机源,根据传入的字节数组
            SecureRandom random = SecureRandom.getInstance("SHA1PRNG");
            random.setSeed(SEED_RULES.getBytes());
            keyGenerator.init(128, random);
            //3.产生原始对称密钥
            SecretKey originalKey = keyGenerator.generateKey();
            //4.获得原始对称密钥的字节数组
            byte[] raw = originalKey.getEncoded();
            //5.根据字节数组生成AES密钥
            SecretKey key = new SecretKeySpec(raw, "AES");
            //6.根据指定算法AES自成密码器
            Cipher cipher = Cipher.getInstance("AES");
            //7.初始化密码器，第一个参数为加密(Encrypt_mode)或者解密解密(Decrypt_mode)操作，第二个参数为使用的KEY
            cipher.init(Cipher.ENCRYPT_MODE, key);
            //8.获取加密内容的字节数组(这里要设置为utf-8)不然内容中如果有中文和英文混合中文就会解密为乱码
            byte[] byteEncode = content.getBytes("utf-8");
            //9.根据密码器的初始化方式--加密：将数据加密
            byte[] byteAES = cipher.doFinal(byteEncode);
            //10.将加密后的数据转换为字符串
            //这里用Base64Encoder中会找不到包
            //解决办法：
            //在项目的Build path中先移除JRE System Library，再添加库JRE System Library，重新编译后就一切正常了。
            String aesEncode = new String(new BASE64Encoder().encode(byteAES));
            //11.将字符串返回
            return aesEncode;
        } catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException | UnsupportedEncodingException | IllegalBlockSizeException | BadPaddingException e) {
            e.printStackTrace();
        }
        //如果有错就返加nulll
        return null;
    }

    /**
     * @Description AES加密
     * @Author Charon [248135724@qq.com]
     * @Date 2018/12/23 15:40
     * @Param [content]
     * @return java.lang.String
     */
    static public final String aesDecode(String content) {
        try {
            //1.构造密钥生成器，指定为AES算法,不区分大小写
            KeyGenerator keygen = KeyGenerator.getInstance("AES");
            //2.根据ecnodeRules规则初始化密钥生成器
            //生成一个128位的随机源,根据传入的字节数组
            SecureRandom random = SecureRandom.getInstance("SHA1PRNG");
            random.setSeed(SEED_RULES.getBytes());
            keygen.init(128, random);
            //3.产生原始对称密钥
            SecretKey originalKey = keygen.generateKey();
            //4.获得原始对称密钥的字节数组
            byte[] raw = originalKey.getEncoded();
            //5.根据字节数组生成AES密钥
            SecretKey key = new SecretKeySpec(raw, "AES");
            //6.根据指定算法AES自成密码器
            Cipher cipher = Cipher.getInstance("AES");
            //7.初始化密码器，第一个参数为加密(Encrypt_mode)或者解密(Decrypt_mode)操作，第二个参数为使用的KEY
            cipher.init(Cipher.DECRYPT_MODE, key);
            //8.将加密并编码后的内容解码成字节数组
            byte[] byteContent = new BASE64Decoder().decodeBuffer(content);
            /*
             * 解密
             */
            byte[] byteDecode = cipher.doFinal(byteContent);
            String aesDecode = new String(byteDecode, "utf-8");
            return aesDecode;
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        } catch (NoSuchPaddingException e) {
            e.printStackTrace();
        } catch (InvalidKeyException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (IllegalBlockSizeException e) {
            throw new RuntimeException("可以解密的密文为，com.charon.common.util.AESUtil工具类加密过的密文！");
        } catch (BadPaddingException e) {
            e.printStackTrace();
        }
        //如果有错就返加nulll
        return null;
    }

    /**
     * @Author Charon [248135724@qq.com]
     * @Description MD5加密
     * @Date 2018/12/23 15:47
     * @Param [content]
     * @return java.lang.String
     */
    static public final String md5(String content) {
        //用于加密的字符
        char[] md5String = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
                'A', 'B', 'C', 'D', 'E', 'F'};
        try {
            //使用平台的默认字符集将此 String 编码为 byte序列，并将结果存储到一个新的byte数组中
            byte[] btInput = content.getBytes();

            //信息摘要是安全的单向哈希函数，它接收任意大小的数据，并输出固定长度的哈希值。
            MessageDigest mdInst = MessageDigest.getInstance("MD5");

            //MessageDigest对象通过使用 update方法处理数据， 使用指定的byte数组更新摘要
            mdInst.update(btInput);

            // 摘要更新之后，通过调用digest（）执行哈希计算，获得密文
            byte[] md = mdInst.digest();

            // 把密文转换成十六进制的字符串形式
            int j = md.length;
            char[] str = new char[j * 2];
            int k = 0;
            for (int i = 0; i < j; i++) {   //  i = 0
                byte byte0 = md[i];  //95
                str[k++] = md5String[byte0 >>> 4 & 0xf];    //    5
                str[k++] = md5String[byte0 & 0xf];   //   F
            }

            //返回经过加密后的字符串
            return new String(str);

        } catch (Exception e) {
            return null;
        }
    }
    /**
     * @Author Charon [248135724@qq.com]
     * @Description Base64编码
     * @Date 2018/12/23 16:05
     * @Param [content]
     * @return java.lang.String
     */
    public static final String base64Encode(String content){
        Base64.Encoder encoder = Base64.getEncoder();

        final byte[] textByte;
        try {
            textByte = content.getBytes("UTF-8");
            return encoder.encodeToString(textByte);
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * @Author Charon [248135724@qq.com]
     * @Description Base64解码
     * @Date 2018/12/23 16:05
     * @Param [content]
     * @return java.lang.String
     */
    public static final String base64Decode(String content){
        Base64.Decoder decoder = Base64.getDecoder();
        return new String(decoder.decode(content));
    }

    /**
     * @Author Charon [248135724@qq.com]
     * @Description 将文件转换成Base64编码
     * @Date 2018/12/23 16:16
     * @Param [file] 文件绝对路径
     * @return java.lang.String
     */
    public static String fileToBase64(String file) {
        //将图片文件转化为字节数组字符串，并对其进行Base64编码处理
        Base64.Encoder encoder = Base64.getEncoder();
        byte[] data = null;
        //读取图片字节数组
        try {
            data = Files.readAllBytes(Paths.get(file));
            return encoder.encodeToString(data);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * @Author Charon [248135724@qq.com]
     * @Description 将Base64转换成文件
     * @Date 2018/12/23 16:20
     * @Param [file] 文件绝对路径
     * @return java.lang.String
     */
    public String base64ToFile(String base64, String filePath) {
        if (base64 == null && filePath == null)return null;
        try {
            //StandardOpenOption.CREATE是处理文件的方式，CREATE是不管路径下有或没有，都创建这个文件，有则覆盖。
            Files.write(Paths.get(filePath), Base64.getDecoder().decode(base64), StandardOpenOption.CREATE);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return filePath;
    }
}
